module klang.编译.编译.编译_;
import 全局;
import ··类型_ = klang.编译.类型.类型_;
import ··诊断_ = klang.编译.诊断.诊断_;
import 扫描 = klang.编译.编译.扫描.扫描_;
import 路径 = klang.编译.路径.路径_;
import 系统 = klang.编译.系统.系统_;
import 核心 = klang.编译.核心.核心_;
import klang.编译.类型.类型_: 取消令牌_, 输出结果_, 绑定函数_, 清理函数_, 转换工厂_, 前端接口_, 写文件回调_, 定制转换组_, 绑定子函数_, 命令行选项_, 输出解析器_, 编译器基类_, 编译选项基类_, 模块解析缓存_, 解析的模块名称_, 检查初始化函数_, 源文件准备函数_, 前端编译器接口_, 初始源文件检查函数_, TransformationResult;
import klang.编译.语法节点.节点_: 节点_, 令牌_, 标识符_, 源文件_, 令牌种类_, 节点数组_, 增量节点_, 创建节点, 遍历节点, 文本字面量_, 数字字面量_, 字面量类节点_;
import klang.编译.json.JSON: JSON值_;
import klang.编译.核心.核心_: 整数_, 词典_;
import klang.编译.类型.枚举值.枚举值_: SK, TK, 字符_, 令牌标志_, 节点标志_;
import klang.编译.常量值.常量值_: 令牌文本数组;
import klang.编译.工具.工具_: 诊断收集_, 创建文件诊断消息;
import klang.编译.诊断集.诊断集_: 诊断集_;
import klang.编译.诊断.诊断_: 诊断_, 诊断消息_;
前端编译器接口_ 编译器实例 = ··null!(前端编译器接口_);
klang.编译.编译.编译_.编译器_ 当前编译器 = ··null!(klang.编译.编译.编译_.编译器_);
前端接口_ 当前前端 = ··null!(前端接口_);
核心.词典_!(dstring) 文本内容缓存;
SK 当前令牌 = SK.未知__;
诊断_[] 编译诊断消息;
long 当前标志 = 0;
long 临时储存的当前标志 = 0;
bool 编译之前的节点有错误 = false;
bool 有JsDoc = false;
TK 当前tk = TK.无_;
static this() {
    文本内容缓存 = new 核心.词典_!(dstring)();
}
class 编译器_ : 编译器基类_ {
    dstring 当前文本;
    路径.路径_ 当前文件名;
    源文件_ 当前源文件;
    long 标识符数量 = 0;
    核心.词典_!(dstring) 标识符缓存组;
    this() { }
    this(dstring 文件名, dstring 内容, 源文件_ 源文件) {
        this.当前文本 = 内容;
        this.当前文件名 = new 路径.路径_(路径.正规化路径(文件名));
        this.标识符缓存组 = 核心.创建词典!(dstring)();
        this.当前源文件 = 源文件;
        this.当前源文件.文件名 = this.当前文件名;
        this.当前源文件.标识符数量 = this.标识符数量;
        this.当前源文件.标识符映射 = this.标识符缓存组;
        扫描.设置扫描器(当前前端.获取扫描设置());
        扫描.setText(this.当前文本, 0, this.当前文本.length);
        扫描.setOnError(cast(扫描.错误回调_)(&this.扫描错误));
    }
    void 初始化(dstring 文件名, dstring 内容, 源文件_ 源文件) {
        this.标识符缓存组 = 核心.创建词典!(dstring)();
        this.当前文件名 = new 路径.路径_(路径.正规化路径(文件名));
        this.当前文本 = 内容;
        this.当前源文件 = 源文件;
        this.当前源文件.文件名 = this.当前文件名;
        this.当前源文件.标识符数量 = this.标识符数量;
        this.当前源文件.标识符映射 = this.标识符缓存组;
        扫描.设置扫描器(当前前端.获取扫描设置());
        扫描.setText(this.当前文本, 0, this.当前文本.length);
        扫描.setOnError(cast(扫描.错误回调_)(&this.扫描错误));
    }
    void 清理() {
        .当前令牌 = SK.未知__;
        .编译诊断消息 = ··null!(诊断_[]);
        .当前标志 = 0;
        .编译之前的节点有错误 = false;
        this.当前文本 = ··null!(dstring);
        this.当前文件名 = ··null!(路径.路径_);
        this.当前源文件 = ··null!(源文件_);
        this.标识符数量 = 0;
        this.标识符缓存组 = 核心.创建词典!(dstring)();
        扫描.恢复扫描工具();
    }
    void 扫描错误(诊断消息_ 消息, long 长) {
        this.报告当前位置编译错误(扫描.getTextPos(), 长, 消息, [" "d]);
    }
    void 报告当前令牌编译错误(诊断消息_ 消息, dstring 消息实参 = " "d) {
        this.编译错误在(扫描.getTokenPos(), 扫描.getTextPos(), 消息, 消息实参);
    }
    void 报告当前位置编译错误(long 起点, long 长, 诊断消息_ 消息, dstring[] 消息实参) {
        消息实参 = 消息实参.dup;
        诊断_ 最后一条错误 = 核心.最后一个!(诊断_)(.编译诊断消息);
        if ((最后一条错误 is ··null!(诊断_)) || 起点 != 最后一条错误.起点) {
            ··诊断_.诊断包括位置_ 诊断 = 创建文件诊断消息(this.当前源文件, 起点, 长, 消息, 消息实参);
            .编译诊断消息 = .追加!(诊断_)(.编译诊断消息, 诊断);
        }
        .编译之前的节点有错误 = true;
    }
    void 编译错误在(long 起点, long 结尾, 诊断消息_ 消息, dstring 消息实参 = " "d) {
        this.报告当前位置编译错误(起点, 结尾 - 起点, 消息, [消息实参]);
    }
    void 编译错误(long 起点, long 结尾, 诊断消息_ 消息, dstring[] 消息实参) {
        消息实参 = 消息实参.dup;
        this.报告当前位置编译错误(起点, 结尾 - 起点, 消息, 消息实参);
    }
    诊断_[] 获取收集的诊断消息() {
        return .编译诊断消息;
    }
    SK 下个令牌() {
        .当前令牌 = 扫描.扫描();
        return .当前令牌;
    }
    SK 令牌() {
        return .当前令牌;
    }
    long 获取令牌开始() {
        return 扫描.getTokenPos();
    }
    long 获取节点开始() {
        return 扫描.getStartPos();
    }
    long 获取文本开始() {
        return 扫描.getTextPos();
    }
    bool 预扫描(dstring sks) {
        return 扫描.预扫描(sks);
    }
    T f·完成节点(T)(T 节点, long 开始, long 结尾 = -2) {
        节点.开始 = 开始;
        if (结尾 == -2) {
            结尾 = 扫描.getStartPos();
        }
        节点.结束 = 结尾;
        if (.当前标志 != 0) {
            节点.标志 |= .当前标志;
        }
        if (.编译之前的节点有错误) {
            .编译之前的节点有错误 = false;
            节点.标志 |= (cast(整数_)(节点标志_.节点有错误));
        }
        return 节点;
    }
    T f·编译令牌节点(T)() {
        long 开始 = this.获取节点开始();
        T 节点 = 创建节点!(T)(this.令牌(), .当前tk);
        this.下个令牌();
        return this.f·完成节点!(T)(节点, 开始);
    }
    令牌_!(SK) 编译文件结尾令牌() {
        long 开始 = this.获取节点开始();
        assert(this.令牌() == SK.文件结尾, "源文件结尾错误"c);
        令牌_!(SK) 节点 = 创建节点!(令牌_!(SK))(this.令牌(), .当前tk);
        return this.f·完成节点!(令牌_!(SK))(节点, 开始);
    }
    令牌_!(TK) f·编译可选令牌节点(TK)(TK t) {
        if (this.令牌() == t) {
            return this.f·编译令牌节点!(令牌_!(TK))();
        }
        return ··null!(令牌_!(TK));
    }
    bool 编译确定的(SK sk, 诊断消息_ 诊断消息 = ··null!(诊断消息_), bool 应该前进 = true) {
        if (this.令牌() == sk) {
            if (应该前进) {
                this.下个令牌();
            }
            return true;
        }
        if (诊断消息 !is ··null!(诊断消息_)) {
            this.报告当前令牌编译错误(诊断消息);
        }
        else {
            dstring 参数 = 令牌文本数组[(cast(int)(sk))];
            this.报告当前令牌编译错误(诊断集_.a0_expected, 参数);
        }
        return false;
    }
    bool 编译可选的(SK sk) {
        if (this.令牌() == sk) {
            this.下个令牌();
            return true;
        }
        return false;
    }
    T f·推断助手(T)(T delegate() 回调, bool 是向前看) {
        SK 储存当前令牌 = .当前令牌;
        size_t 储存编译诊断消息长度 = 编译诊断消息.length;
        bool 储存编译之前的节点有错误 = .编译之前的节点有错误;
        long 储存当前标志 = .当前标志;
        T 结果 = 是向前看 ? 扫描.lookAhead!(T)(cast(T delegate())(回调)) : 扫描.tryScan!(T)(cast(T delegate())(回调));
        assert(储存当前标志 == .当前标志, "推断助手函数 错误"c);
        if ((结果 is ··null!(T)) || 是向前看) {
            .当前令牌 = 储存当前令牌;
            编译诊断消息.length = 储存编译诊断消息长度;
            .编译之前的节点有错误 = 储存编译之前的节点有错误;
        }
        return 结果;
    }
    T f·向前看(T)(T delegate() callback) {
        return this.f·推断助手!(T)(cast(T delegate())(callback), /*isLookAhead*/ true);
    }
    T f·尝试编译(T)(T delegate() callback) {
        return this.f·推断助手!(T)(cast(T delegate())(callback), /*isLookAhead*/ false);
    }
    T f·创建失踪节点(T)(SK sk, bool 报告当前位置错误, 诊断消息_ 诊断消息, dstring 消息实参 = ""d) {
        if (报告当前位置错误) {
            this.报告当前位置编译错误(扫描.getStartPos(), 0, 诊断消息, [消息实参]);
        }
        else if (诊断消息 !is ··null!(诊断消息_)) {
            this.报告当前令牌编译错误(诊断消息, 消息实参);
        }
        long 开始 = this.获取节点开始();
        节点_ 结果 = 创建节点!(节点_)(sk, .当前tk);
        if (sk == SK.标识符) {
            (cast(标识符_)(结果)).内容 = ""d;
        }
        else if (sk == SK.文本字面量) {
            (cast(文本字面量_)(结果)).内容 = ""d;
        }
        else if (sk == SK.数字字面量) {
            (cast(数字字面量_)(结果)).内容 = ""d;
        }
        return (cast(T)(this.f·完成节点!(节点_)(结果, 开始)));
    }
    节点数组_!(T) f·创建失踪列表(T)() {
        节点数组_!(T) 列表 = new 节点数组_!(T)([]);
        列表.是失踪的 = true;
        return 列表;
    }
    节点数组_!(T) f·创建节点数组(T)(T[] 元素组, long 开始, long 结束 = -2) {
        节点数组_!(T) 数组 = new 节点数组_!(T)(元素组);
        数组.开始 = 开始;
        if (结束 == -2) {
            结束 = 扫描.getStartPos();
        }
        数组.结束 = 结束;
        return 数组;
    }
    bool 计算编译错误(节点_ 计算的节点) {
        this.收集子节点数据(计算的节点);
        return (计算的节点.标志 & 节点标志_.节点或子节点有错误) != 0;
    }
    void 收集子节点数据(节点_ 计算的节点) {
        if ((计算的节点.标志 & 节点标志_.已聚合了子节点错误) == 0) {
            bool 节点或任意一个子节点有错误 = ((计算的节点.标志 & 节点标志_.节点有错误) != 0) || 当前前端.获取遍历节点()(计算的节点, cast(bool delegate(节点_))(&this.计算编译错误), ··null!(bool delegate(节点数组_!(节点_))));
            if (节点或任意一个子节点有错误) {
                计算的节点.标志 |= (cast(整数_)(节点标志_.节点或子节点有错误));
            }
            计算的节点.标志 |= (cast(整数_)(节点标志_.已聚合了子节点错误));
        }
    }
    令牌标志_ 获取令牌标志() {
        return 扫描.getTokenFlags();
    }
    dstring 获取令牌值() {
        return 扫描.getTokenValue();
    }
    dstring 获取令牌文本() {
        return 扫描.getTokenText();
    }
    标识符_ 创建标识符(bool 是标识符, 诊断消息_ 诊断消息 = ··null!(诊断消息_)) {
        ++this.标识符数量;
        if (是标识符) {
            long 开始 = this.获取节点开始();
            标识符_ 标识符 = 创建节点!(标识符_)(SK.标识符, .当前tk);
            标识符.内容 = this.初始标识符文本(扫描.getTokenValue());
            this.下个令牌();
            return this.f·完成节点!(标识符_)(标识符, 开始);
        }
        // Only for end of file because the error gets reported incorrectly on embedded script tags.
        bool 报告当前位置 = this.令牌() == SK.文件结尾;
        dstring 消息文本 = 扫描.getTokenText();
        诊断消息_ 默认消息 = 诊断集_.Identifier_expected;
        if (诊断消息 is ··null!(诊断消息_)) {
            诊断消息 = 默认消息;
        }
        return this.f·创建失踪节点!(标识符_)(SK.标识符, 报告当前位置, 诊断消息, 消息文本);
    }
    dstring 初始标识符文本(dstring 内容) {
        dstring 缓存 = this.初始文本值(内容);
        if (!this.标识符缓存组.具有(缓存)) {
            this.标识符缓存组.p·设置(内容, 缓存);
        }
        return this.标识符缓存组.获取(内容);
    }
    dstring 初始文本值(dstring 内容) {
        if (!文本内容缓存.具有(内容)) {
            文本内容缓存.p·设置(内容, 内容);
        }
        return 文本内容缓存.获取(内容);
    }
    文本字面量_ 编译文本字面量() {
        字面量类节点_ 节点 = this.编译字面量类节点(SK.文本字面量);
        return (cast(文本字面量_)(节点));
    }
    数字字面量_ 编译数字字面量() {
        字面量类节点_ 节点 = this.编译字面量类节点(SK.数字字面量);
        return (cast(数字字面量_)(节点));
    }
    bool 有转义字符() {
        return 扫描.hasUnicodeEscape();
    }
    bool 有扩展转义字符() {
        return 扫描.hasExtendedUnicodeEscape();
    }
    bool 是未完成的() {
        return 扫描.isUnterminated();
    }
    字面量类节点_ 编译字面量类节点(SK sk) {
        long 开始 = this.获取节点开始();
        字面量类节点_ 节点 = 创建节点!(字面量类节点_)(sk, .当前tk);
        节点.内容 = this.初始文本值(扫描.getTokenValue());
        dstring 令牌文本 = 扫描.getTokenText();
        if (令牌文本.length >= 2) {
            节点.原始文本 = 令牌文本[1 .. 令牌文本.length - (this.是未完成的() ? 0 : 1)];
        }
        if (扫描.hasExtendedUnicodeEscape()) {
            节点.有万维码转义 = true;
        }
        if (this.是未完成的()) {
            节点.是未完成的 = true;
        }
        this.下个令牌();
        this.f·完成节点!(字面量类节点_)(节点, 开始);
        return 节点;
    }
    bool 前面有换行符() {
        return 扫描.前面有换行符();
    }
    dstring 获取当前字面量引号() {
        return 扫描.获取当前字面量引号();
    }
    bool 是出现了关注字符() {
        return 扫描.是出现了关注字符();
    }
    void 设置当前标志(bool 值, long 标志) {
        if (值) {
            .当前标志 |= 标志;
        }
        else {
            .当前标志 &= ~标志;
        }
    }
    T f·在上下文约束内编译(T)(long 上下文, T delegate() 回调) {
        long 要设置的上下文标志 = 上下文 & ~.当前标志;
        if (要设置的上下文标志 != 0) {
            this.设置当前标志(true, 要设置的上下文标志);
            T 结果 = 回调();
            this.设置当前标志(false, 要设置的上下文标志);
            return 结果;
        }
        return 回调();
    }
    bool 是在上下文内(long 标志) {
        return (.当前标志 & 标志) != 0;
    }
    long 获取当前标志() {
        return .当前标志;
    }
    void 设置是否有JsDoc(bool 值) {
        .有JsDoc = 值;
    }
    T f·扫描范围(T)(long start, long 长度, T delegate() 回调) {
        return 扫描.scanRange!(T)(start, 长度, cast(T delegate())(回调));
    }
    void 储存当前标志() {
        .临时储存的当前标志 = .当前标志;
    }
    void 恢复当前标志() {
        .当前标志 = .临时储存的当前标志;
    }
}
源文件_ 创建源文件(dstring 文件名, dstring 内容, 前端接口_ 文件前端, bool 固定父节点 = false, 解析的模块名称_ 解析的 = ··null!(解析的模块名称_)) {
    前端编译器接口_ 编译器 = .创建编译器(文件名, 内容, 文件前端, 解析的);
    源文件_ 源文件 = 编译器.编译源文件();
    源文件.标志 |= .当前标志;
    源文件.编译诊断 = .编译诊断消息;
    编译器.清理();
    if (固定父节点) {
        .固定父引用(源文件);
    }
    return 源文件;
}
前端编译器接口_ 创建编译器(dstring 文件名, dstring 内容, 前端接口_ 文件前端, 解析的模块名称_ 解析的 = ··null!(解析的模块名称_)) {
    if ((.编译器实例 is ··null!(前端编译器接口_)) || .当前前端 != 文件前端) {
        .当前前端 = 文件前端;
        .当前tk = 当前前端.获取前端目标();
        扫描.创建扫描(true);
        .当前编译器 = new 编译器_(文件名, 内容, 文件前端.创建源文件(文件名, 内容, 解析的));
        .编译器实例 = 文件前端.创建前端编译器(.当前编译器);
    }
    else {
        扫描.创建扫描(true);
        当前编译器.初始化(文件名, 内容, 文件前端.创建源文件(文件名, 内容, 解析的));
    }
    return .编译器实例;
}
void 遍历源文件(节点_ n, bool delegate(节点_) cbNode) {
    当前前端.获取遍历节点()(n, cast(bool delegate(节点_))(cbNode), ··null!(bool delegate(节点数组_!(节点_))));
}
void 固定父引用(节点_ 根节点) {
    bool delegate(节点_) 访问节点;
    节点_ 父节点;
    bool 访问节点·func(节点_ n) {
        if (n.父节点 != 父节点) {
            n.父节点 = 父节点;
            节点_ 储存父 = 父节点;
            父节点 = n;
            .遍历源文件(n, cast(bool delegate(节点_))(访问节点));
            父节点 = 储存父;
        }
        return false;
    }
    访问节点 = &访问节点·func;
    父节点 = 根节点;
    .遍历源文件(根节点, cast(bool delegate(节点_))(访问节点));
    return;
}
